package com.hundsun.cprs.yyt.service.goods.service.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.hundsun.cprs.yyt.util.DoubleUtil;
import com.sun.tools.javac.util.Assert;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.Validate;
import org.apache.http.util.Asserts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSON;
import com.hundsun.cprs.yyt.common.base.BaseService;
import com.hundsun.cprs.yyt.common.enums.EnumAreaCode;
import com.hundsun.cprs.yyt.common.enums.EnumBusinessCode;
import com.hundsun.cprs.yyt.common.exception.ServiceCodeException;
import com.hundsun.cprs.yyt.service.goods.dao.GoodsDao;
import com.hundsun.cprs.yyt.service.goods.dao.GoodsSpecsDao;
import com.hundsun.cprs.yyt.service.goods.domain.GoodsSpecs;
import com.hundsun.cprs.yyt.service.goods.domain.GoodsStockLog;
import com.hundsun.cprs.yyt.service.goods.domain.GoodsWithArea;
import com.hundsun.cprs.yyt.service.goods.dto.AttributeForm;
import com.hundsun.cprs.yyt.service.goods.dto.AttributeInfo;
import com.hundsun.cprs.yyt.service.goods.dto.GoodsInfo;
import com.hundsun.cprs.yyt.service.goods.dto.GoodsPublishInfo;
import com.hundsun.cprs.yyt.service.goods.enums.EnumGoodsStatus;
import com.hundsun.cprs.yyt.service.goods.enums.EnumStockType;
import com.hundsun.cprs.yyt.service.goods.service.GoodsSkuService;
import com.hundsun.cprs.yyt.service.goods.service.GoodsStockLogService;
import com.hundsun.cprs.yyt.service.goods.service.GoodsWithAreaService;

@Service
public class GoodsSkuServiceImpl extends BaseService implements GoodsSkuService {

    @Autowired
    private GoodsSpecsDao goodsSpecsDao;
    @Autowired
    private GoodsDao goodsDao;
    @Autowired
    private GoodsWithAreaService goodsWithAreaService;
    @Autowired
    private GoodsStockLogService goodsStockLogService;
    @Value("${newPlateCode}")
    private String newPlateCode;//新人专场编码
    
    @Override
    @Transactional
    public void insert(GoodsSpecs record) {
    	Asserts.notNull(record, "参数必填");
    	Asserts.notNull(record.getGoodsId(), "商品参数必填");
    	Asserts.notNull(record.getAreaCode(), "专区参数必填");
    	
        if (record.getAllocatedStock() == null) {
            record.setAllocatedStock(BigDecimal.ZERO.intValue());
        }
        if (record.getGoodsStock() == null) {
            record.setGoodsStock(BigDecimal.ZERO.intValue());
        }
        if (record.getPlateCode() == null) {
            record.setPlateCode(BigDecimal.ZERO.toString());
        }
        if (record.getQuotaAmount() == null) {
            record.setQuotaAmount(BigDecimal.ZERO.intValue());
        }
        if (record.getFrozenStock() == null) {
            record.setFrozenStock(BigDecimal.ZERO.intValue());
        }
        if (record.getGoodsPrice() == null) {
        	record.setGoodsPrice(BigDecimal.ZERO.doubleValue());
		}
        goodsSpecsDao.insert(record);
    }
    
	@Override
	@Transactional
	public void insertList(List<GoodsSpecs> recordList) {
		if (recordList == null || recordList.isEmpty()) {
			return;
		}
		
		for (GoodsSpecs item : recordList) {
			insert(item);
			
			if (item.getGoodsStock() > 0) {
				//初始库存
				createStockLog(item);
			}
		}
	}

    @Override
    public GoodsSpecs selectByPrimaryKey(Long id) {
        return goodsSpecsDao.selectByPrimaryKey(id);
    }

    @Override
    @Transactional
    public void updateSkuRecord(GoodsSpecs record) {
        try{
            //版本号迭代，基于乐观锁控制
            int result = goodsSpecsDao.updateByPrimaryKeySelective(record);
            if (result == 0){
                throw new ServiceCodeException(EnumBusinessCode.BUSINESS_1026.getErrorNo(),EnumBusinessCode.BUSINESS_1026.getErrorInfo());
            }
        }catch (ServiceCodeException e){
            logger.error("基于乐观锁控制的sku属性更新失败：修改的记录条数为0，skuId：" + record.getId());
            throw e;
        }catch (Exception e){
            throw e;
        }
    }

    @Override
    public List<GoodsSpecs> selectGoodsSpecsList(Integer goodsId,String areaCode) {
        GoodsSpecs goodsSpecs = new GoodsSpecs();
        goodsSpecs.setGoodsId(goodsId);
        goodsSpecs.setAreaCode(areaCode);
        return goodsSpecsDao.selectGoodsSpecsList(goodsSpecs);
    }

    @Override
    public void delete(Long skuId) {
        goodsSpecsDao.deleteByPrimaryKey(skuId);
    }

    @Override
    @Transactional(isolation=Isolation.READ_COMMITTED)
    public void addAllocatedStock(Long skuId, Integer amount) {
        GoodsSpecs sku = selectByPrimaryKey(skuId);
        int count = 0;
        if (sku.getParent() != null) {
            //委托父SKU预占库存,同时更新冻结库存数量
            count = goodsSpecsDao.addAllocatedStock(sku.getParent().getId(), amount,amount);
        } else {
            count = goodsSpecsDao.addAllocatedStockWithFrozen(skuId, amount);
        }
        if (count == 0) {
            throw new ServiceCodeException(EnumBusinessCode.BUSINESS_1510.getErrorNo(),EnumBusinessCode.BUSINESS_1510.getErrorInfo());
        }
    }
    
    @Override
    @Transactional(isolation=Isolation.READ_COMMITTED)
    public void deductGoodsStock(Long skuId, int amount, int goodsStock) {
        GoodsSpecs sku = selectByPrimaryKey(skuId);
        Long logSkuId = skuId;
        if (sku.getParent() != null) {
            logSkuId = sku.getParent().getId();
        } 
        int count = goodsSpecsDao.deductGoodsStock(logSkuId, amount, goodsStock);
        if (count == 0) {
            throw new ServiceCodeException(EnumBusinessCode.BUSINESS_1510.getErrorNo(),EnumBusinessCode.BUSINESS_1510.getErrorInfo());
        }
        //生成库存变更流水
        GoodsSpecs dbSku = selectByPrimaryKey(logSkuId);
        GoodsStockLog goodsStockLog = new GoodsStockLog();
        goodsStockLog.setSkuId(dbSku.getId());
        goodsStockLog.setCurrentStock(dbSku.getGoodsStock());
        goodsStockLog.setOutquantity(goodsStock);
        goodsStockLog.setType(EnumStockType.OUT_TYPE.getCode());
        goodsStockLog.setAreaCode(dbSku.getAreaCode());
        goodsStockLogService.createStockLog(goodsStockLog);
    }
    
    @Override
    @Transactional(isolation=Isolation.READ_COMMITTED)
    public void deductGoodsStock(Long skuId, int goodsStock) {
        int result = goodsSpecsDao.updateGoodsStock(skuId, -1 * goodsStock);
        if (result == 0) {
            throw new ServiceCodeException(EnumBusinessCode.BUSINESS_1000.getErrorNo(),"专区商品SKU信息不正确");
        }
        
        //生成库存变更流水
        GoodsSpecs updatedSku = selectByPrimaryKey(skuId);
        GoodsStockLog goodsStockLog = new GoodsStockLog();
        goodsStockLog.setSkuId(updatedSku.getId());
        goodsStockLog.setCurrentStock(updatedSku.getGoodsStock());
        goodsStockLog.setOutquantity(goodsStock);
        goodsStockLog.setType(EnumStockType.OUT_TYPE.getCode());
        goodsStockLog.setAreaCode(updatedSku.getAreaCode());
        goodsStockLogService.createStockLog(goodsStockLog);
    }

    @Override
    public GoodsSpecs selectDistinctSku(String areaCode, int goodsId, String plateCode) {
        GoodsSpecs goodsSpecs = new GoodsSpecs();
        goodsSpecs.setPlateCode(plateCode);
        goodsSpecs.setGoodsId(goodsId);
        goodsSpecs.setAreaCode(areaCode);
        List<GoodsSpecs> skuList = goodsSpecsDao.selectGoodsSpecsList(goodsSpecs);
        if (skuList.isEmpty()){
            return null;
        }else{
            return skuList.get(0);
        }
    }

    @Override
    @Transactional
    public void crateWholesaleSku(GoodsPublishInfo goodsPublishInfo) {
        //1.解析提交的属性列表，转换成json
        List<GoodsSpecs> goodsSpecsList = new ArrayList<>();
        parseAttributeToJson(goodsPublishInfo,goodsSpecsList);
        //2.校验规格
        validateSkuList(goodsSpecsList);
        //3.新增商品规格
        createGoodsSpecs(goodsPublishInfo,goodsSpecsList);
        //4.更新商品表的attribute_list字段
        GoodsInfo goodsInfo = new GoodsInfo();
        goodsInfo.setId(goodsPublishInfo.getGoodsId());
        goodsInfo.setAttributeList(goodsPublishInfo.getAttributeList());
        goodsDao.updateByPrimaryKeySelective(goodsInfo);
        //5.新增商品和区的绑定关系
        GoodsWithArea goodsWithArea = new GoodsWithArea();
        goodsWithArea.setAreaCode(EnumAreaCode.WHOLESALE_AREA.getCode());
        goodsWithArea.setGoodsId(goodsPublishInfo.getGoodsId());
        goodsWithArea.setGoodsStatus(EnumGoodsStatus.PUBLISHED.getCode());
        goodsWithArea.setPublishTime(new Date());
        goodsWithArea.setTopShow(0);//默认该商品上架之后不是爆款
        goodsWithAreaService.insert(goodsWithArea);
    }

    protected void parseAttributeToJson(GoodsPublishInfo goodsPublishInfo, List<GoodsSpecs> goodsSpecsList){
        if (goodsPublishInfo.getAttributeInfoList() != null && !goodsPublishInfo.getAttributeInfoList().isEmpty()){
            Map<String,Set<String>> attributeMap = new LinkedHashMap();//商品属性列表转json
            Map<String,String> goodsSpecsMap = new LinkedHashMap();//商品规格转json
            for (AttributeInfo attributeInfo : goodsPublishInfo.getAttributeInfoList()){
                for (AttributeForm attributeForm : attributeInfo.getAttributeFormList()) {
                    Set<String> attributeValueList = attributeMap.get(attributeForm.getAttributeName());
                    if (attributeValueList == null){
                        attributeValueList = new HashSet<>();
                        attributeValueList.add(attributeForm.getAttributeValue());
                    }else{
                        attributeValueList.add(attributeForm.getAttributeValue());
                    }
                    attributeMap.put(attributeForm.getAttributeName(),attributeValueList);
                    goodsSpecsMap.put(attributeForm.getAttributeName(),attributeForm.getAttributeValue());
                }
                //往规格集合中插入数据
                GoodsSpecs goodsSpecs = new GoodsSpecs();
                goodsSpecs.setId(attributeInfo.getSkuId());
                if (attributeInfo.getItemPrice() != null){
                    goodsSpecs.setGoodsPrice(DoubleUtil.mul(attributeInfo.getItemPrice(),100D));
                }
                goodsSpecs.setGoodsStock(attributeInfo.getStockNum());
                goodsSpecs.setQuotaAmount(attributeInfo.getQuotaAmount());
                goodsSpecs.setGoodsSpecs(JSON.toJSONString(goodsSpecsMap));
                goodsSpecsList.add(goodsSpecs);
            }
            goodsPublishInfo.setAttributeList(JSON.toJSONString(attributeMap));
        }
    }

    protected  void createGoodsSpecs(GoodsPublishInfo goodsPublishInfo,List<GoodsSpecs> goodsSpecsList){
        if (!goodsSpecsList.isEmpty()){
            for (GoodsSpecs goodsSpecs:goodsSpecsList){
                //新增规格
                goodsSpecs.setGoodsId(goodsPublishInfo.getGoodsId());
                goodsSpecs.setAreaCode(EnumAreaCode.WHOLESALE_AREA.getCode());
                goodsSpecs.setPlateCode("0");
                this.insert(goodsSpecs);
                //新增库存变更流水记录
                createStockLog(goodsSpecs);
            }
        }
    }

    @Override
    @CacheEvict(value="YYT:AREA:GOODS",key="#goodsPublishInfo.areaCode.toUpperCase() + ':' + new String(#goodsPublishInfo.goodsId)")
    @Transactional
    public void editWholesaleSku(GoodsPublishInfo goodsPublishInfo) {
        //1.解析提交的属性列表，转换成json
        List<GoodsSpecs> goodsSpecsList = new ArrayList<>();
        parseAttributeToJson(goodsPublishInfo,goodsSpecsList);
        //2.校验规格
        validateSkuList(goodsSpecsList);
        //3.修改商品规格,并生成库存变更流水
        updateGoodsSpecs(goodsPublishInfo,goodsSpecsList);
    }


    protected void updateGoodsSpecs(GoodsPublishInfo goodsPublishInfo,List<GoodsSpecs> goodsSpecsList){
        if (!goodsSpecsList.isEmpty()){
            //先获取历史规格列表
            List<GoodsSpecs> skuList = this.selectGoodsSpecsList(goodsPublishInfo.getGoodsId(),EnumAreaCode.WHOLESALE_AREA.getCode());
            Map<Long,GoodsSpecs> skuMap = com.hundsun.cprs.yyt.util.BeanUtils.mapByKey("id", skuList);
            //Set<Long> skuIdSet = new HashSet<>();
            for (GoodsSpecs goodsSpecs:goodsSpecsList){
                if (goodsSpecs.getId() != null && goodsSpecs.getId() > 0) {
                    GoodsSpecs dbSku = skuMap.remove(goodsSpecs.getId());
                    //计算变化后的商品库存 = 可售库存+预占库存
                    Integer changedGoodsStock = goodsSpecs.getGoodsStock() + dbSku.getAllocatedStock();
                    //计算变化量
                    Integer changeAmount =  changedGoodsStock - dbSku.getGoodsStock();
                    dbSku.setGoodsStock(changedGoodsStock);
                    dbSku.setGoodsPrice(goodsSpecs.getGoodsPrice());
                    dbSku.setQuotaAmount(goodsSpecs.getQuotaAmount());
                    dbSku.setGoodsSpecs(goodsSpecs.getGoodsSpecs());
                    
                    updateSkuRecord(dbSku);
                    //记录库存变更流水
                    createStockLog(dbSku,changeAmount);
                } else {
                    goodsSpecs.setGoodsId(goodsPublishInfo.getGoodsId());
                    goodsSpecs.setAreaCode(EnumAreaCode.WHOLESALE_AREA.getCode());
                    goodsSpecs.setPlateCode("0");
                    
                    insert(goodsSpecs);
                    //记录库存变更流水
                    createStockLog(goodsSpecs,goodsSpecs.getGoodsStock());
                }
            }
            //删除规格
            for (GoodsSpecs sku : skuMap.values()){
                this.delete(sku.getId());
            }
        }
    }

    /**
     * 库存流水记录生成
     *
     * */
    private void createStockLog(GoodsSpecs sku,Integer changeAmount){
        if (Math.abs(changeAmount) == 0) {
            return;
        }
        GoodsStockLog goodsStockLog = new GoodsStockLog();
        goodsStockLog.setSkuId(sku.getId());
        goodsStockLog.setCurrentStock(sku.getGoodsStock());
        goodsStockLog.setAreaCode(sku.getAreaCode());
        if (changeAmount > 0) {
            goodsStockLog.setInquantity(Math.abs(changeAmount));
            goodsStockLog.setOutquantity(0);
            goodsStockLog.setType(EnumStockType.IN_TYPE.getCode());
        } else {
            goodsStockLog.setInquantity(0);
            goodsStockLog.setOutquantity(Math.abs(changeAmount));
            goodsStockLog.setType(EnumStockType.OUT_TYPE.getCode());
            goodsStockLog.setAreaCode(sku.getAreaCode());
        }
        goodsStockLogService.createStockLog(goodsStockLog);
    }

    /**
     * 第一次生成库存流水记录
     * 重载方法
     *
     * */
    public void createStockLog(GoodsSpecs newSpecs){
        GoodsStockLog goodsStockLog = new GoodsStockLog();
        goodsStockLog.setSkuId(newSpecs.getId());
        goodsStockLog.setCurrentStock(newSpecs.getGoodsStock());
        goodsStockLog.setInquantity(newSpecs.getGoodsStock());
        goodsStockLog.setOutquantity(0);
        goodsStockLog.setType(EnumStockType.IN_TYPE.getCode());
        goodsStockLog.setAreaCode(newSpecs.getAreaCode());
        goodsStockLogService.createStockLog(goodsStockLog);
    }

    @Override
    public List<Long> selectSkuIdListByGoodsCode(String goodsCode) {
        return goodsSpecsDao.selectSkuIdListByGoodsCode(goodsCode);
    }

    /**
     * 校验前端提交的商品规格是否完整
     * @param goodsSpecsList
     *
     * */
    protected void validateSkuList(List<GoodsSpecs> goodsSpecsList){
        if (goodsSpecsList.isEmpty()){
            throw new ServiceCodeException(EnumBusinessCode.BUSINESS_1305.getErrorNo(),EnumBusinessCode.BUSINESS_1305.getErrorInfo());
        }else{
            for (GoodsSpecs goodsSpecs : goodsSpecsList){
                Validate.notNull(goodsSpecs.getGoodsStock(), "库存不能为空");
                Validate.notNull(goodsSpecs.getGoodsPrice(), "折扣价格不能为空");
                Validate.notNull(goodsSpecs.getQuotaAmount(), "所需配额不能为空");
            }
        }
    }

    @Override
    public List<GoodsSpecs> selectGoodsSpecsList(Integer goodsId, String areaCode, String plateCode) {
        GoodsSpecs goodsSpecs = new GoodsSpecs();
        goodsSpecs.setGoodsId(goodsId);
        goodsSpecs.setAreaCode(areaCode);
        List<GoodsSpecs> skuList = null;
        if (plateCode != null && plateCode.equals(newPlateCode)){
            //优先返回专场维度的SKU信息,若不存在则返回专区下的SKU
            goodsSpecs.setPlateCode(plateCode);//新人专场sku
            skuList = goodsSpecsDao.selectGoodsSpecsList(goodsSpecs);
        }
        
        if (skuList == null || skuList.isEmpty()) {
            goodsSpecs.setPlateCode(BigDecimal.ZERO.toString());
            skuList = goodsSpecsDao.selectGoodsSpecsList(goodsSpecs);
        }
        return skuList;
    }

    @Override
    @Transactional
    public void addGoodsStock(Long goodsId, String areaCode, int goodsStock) {
        if (EnumAreaCode.WHOLESALE_AREA.getCode().equals(areaCode)) {
            throw new UnsupportedOperationException("不支持批发区商品维度的入库操作");
        }
        List<GoodsSpecs> skuList = selectGoodsSpecsList(goodsId.intValue(), areaCode,BigDecimal.ZERO.toString());
        if (CollectionUtils.isEmpty(skuList) || skuList.size() > 1) {
            throw new ServiceCodeException(EnumBusinessCode.BUSINESS_1000.getErrorNo(),"专区商品SKU信息不正确");
        }
        
        GoodsSpecs sku = skuList.get(0);
        goodsSpecsDao.updateGoodsStock(sku.getId(), goodsStock);
        
        GoodsSpecs dbSku = goodsSpecsDao.selectByPrimaryKey(sku.getId());
        //入库记录
        GoodsStockLog goodsStockLog = new GoodsStockLog();
        goodsStockLog.setSkuId(dbSku.getId());
        goodsStockLog.setCurrentStock(dbSku.getGoodsStock());
        goodsStockLog.setInquantity(goodsStock);
        goodsStockLog.setOutquantity(0);
        goodsStockLog.setType(EnumStockType.IN_TYPE.getCode());
        goodsStockLog.setAreaCode(areaCode);
        goodsStockLogService.createStockLog(goodsStockLog);
    }
    
    @Override
    @Transactional(isolation=Isolation.READ_COMMITTED)
    public void addGoodsStock(Long skuId, int goodsStock) {
        int result = goodsSpecsDao.updateGoodsStock(skuId, goodsStock);
        if (result == 0) {
            throw new ServiceCodeException(EnumBusinessCode.BUSINESS_1000.getErrorNo(),"专区商品SKU信息不正确");
        }
        
        //入库记录
        GoodsSpecs updatedSku = selectByPrimaryKey(skuId);
        GoodsStockLog goodsStockLog = new GoodsStockLog();
        goodsStockLog.setSkuId(updatedSku.getId());
        goodsStockLog.setCurrentStock(updatedSku.getGoodsStock());
        goodsStockLog.setInquantity(goodsStock);
        goodsStockLog.setOutquantity(0);
        goodsStockLog.setType(EnumStockType.IN_TYPE.getCode());
        goodsStockLog.setAreaCode(updatedSku.getAreaCode());
        goodsStockLogService.createStockLog(goodsStockLog);
    }

    @Override
    public void reduceGoodsStock(Long goodsId, String areaCode, int goodsStock) {
        if (EnumAreaCode.WHOLESALE_AREA.getCode().equals(areaCode)) {
            throw new UnsupportedOperationException("不支持批发区商品维度的出库操作");
        }
        List<GoodsSpecs> skuList = selectGoodsSpecsList(goodsId.intValue(), areaCode,BigDecimal.ZERO.toString());
        if (CollectionUtils.isEmpty(skuList) || skuList.size() > 1) {
            throw new ServiceCodeException(EnumBusinessCode.BUSINESS_1000.getErrorNo(),"专区商品SKU信息不正确");
        }
        
        GoodsSpecs sku = skuList.get(0);
        goodsSpecsDao.updateGoodsStock(sku.getId(), -1 * goodsStock);
        
        GoodsSpecs dbSku = goodsSpecsDao.selectByPrimaryKey(sku.getId());
        //出库记录
        GoodsStockLog goodsStockLog = new GoodsStockLog();
        goodsStockLog.setSkuId(dbSku.getId());
        goodsStockLog.setCurrentStock(dbSku.getGoodsStock());
        goodsStockLog.setInquantity(0);
        goodsStockLog.setOutquantity(goodsStock);
        goodsStockLog.setType(EnumStockType.OUT_TYPE.getCode());
        goodsStockLog.setAreaCode(areaCode);
        goodsStockLogService.createStockLog(goodsStockLog);
    }

    @Override
    @Transactional
    public void updateFrozenStock(GoodsSpecs goodsSpecs) {
        goodsSpecsDao.updateFrozenStock(goodsSpecs.getId(), goodsSpecs.getFrozenStock());
    }
    
}
